// Project: Salient
// http://salient.codeplex.com
// 
// Copyright 2010, Sky Sanders <sky at skysanders.net>
// Dual licensed under the MIT or GPL Version 2 licenses.
// http://salient.codeplex.com/license
// 
// Date: May 5 2010 

#region

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Data.EntityClient;
using System.Diagnostics;
using System.Security.Permissions;
using System.Web;
using System.Web.Management;
using Salient.Web.DataAccess;
using Salient.Web.SQLite.Model;
using Salient.Web.Util;

#endregion

namespace Salient.Web.Management
{
    ///<summary>
    ///</summary>
    public class SQLiteWebEventProvider : BufferedWebEventProvider
    {
        #region Fields

        private string _entityConnectionString = String.Empty;

        #endregion

        #region Properties

        ///<summary>
        ///</summary>
        public string EntityConnectionString
        {
            get { return _entityConnectionString; }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Sets the initial values for this object.
        /// </summary>
        /// <param name="name">The name used in the configuration file to identify this provider.
        ///                 </param><param name="config">A <see cref="T:System.Collections.Specialized.NameValueCollection"/> that specifies the attributes assigned for this provider in the configuration file.
        ///                 </param>
        [PermissionSet(SecurityAction.InheritanceDemand, Unrestricted = true)]
        //[AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
        [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            if (name.Length < 1)
            {
                name = "SQLiteWebEventProvider";
            }

            if (string.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", SR.GetString(SR.WebEventSqlProviderDescription));
            }

            _entityConnectionString = config["connectionStringName"];

            if (string.IsNullOrEmpty(_entityConnectionString))
            {
                throw new ProviderException(SR.GetString(SR.ConnectionNameNotSpecified));
            }

            SQLiteEntityConnectionHolder.GetConnection(_entityConnectionString);

            config.Remove("connectionStringName");

            base.Initialize(name, config);

            if (config.Count > 0)
            {
                string attribUnrecognized = config.GetKey(0);
                if (!String.IsNullOrEmpty(attribUnrecognized))
                {
                    throw new ProviderException(SR.GetString(SR.ProviderUnrecognizedAttribute, attribUnrecognized));
                }
            }
        }

        /// <summary>
        /// Processes the event passed to the provider.
        /// </summary>
        /// <param name="eventRaised">The <see cref="T:System.Web.Management.WebBaseEvent"/> object to process.
        ///                 </param>
        public override void ProcessEvent(WebBaseEvent eventRaised)
        {
            if (UseBuffering)
            {
                if (Debugger.IsAttached)
                {
                    Trace.WriteLine(string.Format("ProcessEvent - Buffered - {0} {1}", eventRaised.EventCode,
                                                  eventRaised.Message));
                }
                base.ProcessEvent(eventRaised);
            }
            else
            {
                if (Debugger.IsAttached)
                {
                    Trace.WriteLine(string.Format("ProcessEvent - {0} {1}", eventRaised.EventCode, eventRaised.Message));
                }
                PersistEvents(new SQLiteWebEventCollection(eventRaised), 0, new DateTime(0));
            }
        }

        /// <summary>
        /// Processes the buffered events.
        /// </summary>
        /// <param name="flushInfo">A <see cref="T:System.Web.Management.WebEventBufferFlushInfo"/> that contains buffering information.
        ///                 </param>
        public override void ProcessEventFlush(WebEventBufferFlushInfo flushInfo)
        {
            if (Debugger.IsAttached)
            {
                Trace.WriteLine(string.Format("EventBufferFlush - {0} events - discarded {1} - LastNotificationUtc {2}",
                                              flushInfo.Events.Count, flushInfo.EventsDiscardedSinceLastNotification,
                                              flushInfo.LastNotificationUtc));
            }
            PersistEvents(flushInfo.Events, flushInfo.EventsDiscardedSinceLastNotification,
                          flushInfo.LastNotificationUtc);
        }

        /// <summary>
        /// Performs tasks associated with shutting down the provider.
        /// </summary>
        public override void Shutdown()
        {
            try
            {
                Flush();
            }
            finally
            {
                base.Shutdown();
            }

            //// Need to wait until all connections are gone before returning here
            //// Sleep for 2x the command timeout in 1 sec intervals then give up, default timeout is 30 sec
            //if (_connectionCount > 0)
            //{
            //    int sleepAttempts = _commandTimeout * 2;
            //    if (sleepAttempts <= 0)
            //    {
            //        sleepAttempts = 60;
            //    }
            //    // Check every second
            //    while (_connectionCount > 0 && sleepAttempts > 0)
            //    {
            //        --sleepAttempts;
            //        Thread.Sleep(1000);
            //    }
            //}
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// 
        /// </summary>
        /// <param name="raisedEvents"></param>
        protected virtual void EventProcessingComplete(ICollection raisedEvents)
        {
        }

        #endregion

        #region Private Methods

        [PermissionSet(SecurityAction.InheritanceDemand, Unrestricted = true)]
        [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
        [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
        private void PersistEvents(ICollection events, int eventsDiscardedByBuffer, DateTime lastNotificationUtc)
        {
            // TODO: should log eventsDiscardedByBuffer?
            Trace.WriteLine(string.Format("PersistEvents - {0} events - discarded {1} - LastNotificationUtc {2}",
                              events.Count, eventsDiscardedByBuffer,
                              lastNotificationUtc));
            using (
                SQLiteEntityConnectionHolder holder = SQLiteEntityConnectionHolder.GetConnection(_entityConnectionString)
                )
            {
                using (AspNetDbEntities ctx = new AspNetDbEntities(holder.Connection))
                {
                    using (EntityTransaction tx = holder.Connection.BeginTransaction())
                    {
                        try
                        {
                            WebApplicationInformation appInfo = WebBaseEvent.ApplicationInformation;

                            foreach (WebBaseEvent eventRaised in events)
                            {
                                WebRequestInformation reqInfo = null;
                                Exception exception = null;

                                if (eventRaised is WebRequestEvent)
                                {
                                    reqInfo = ((WebRequestEvent)eventRaised).RequestInformation;
                                }
                                else if (eventRaised is WebRequestErrorEvent)
                                {
                                    reqInfo = ((WebRequestErrorEvent)eventRaised).RequestInformation;
                                }
                                else if (eventRaised is WebErrorEvent)
                                {
                                    reqInfo = ((WebErrorEvent)eventRaised).RequestInformation;
                                }
                                else if (eventRaised is WebAuditEvent)
                                {
                                    reqInfo = ((WebAuditEvent)eventRaised).RequestInformation;
                                }
                                string requestUrl = (reqInfo != null) ? reqInfo.RequestUrl : null;

                                if (eventRaised is WebBaseErrorEvent)
                                {
                                    exception = ((WebBaseErrorEvent)eventRaised).ErrorException;
                                }

                                string exceptionType =
                                    (exception != null) ? exception.GetType().ToString() : null;

                                string details = eventRaised.ToString();
                                WebEvent evt = new WebEvent
                                    {
                                        ApplicationPath = appInfo.ApplicationPath,
                                        ApplicationVirtualPath = appInfo.ApplicationVirtualPath,
                                        EventCode = eventRaised.EventCode,
                                        EventDetailCode = eventRaised.EventDetailCode,
                                        EventId = eventRaised.EventID.ToString(),
                                        EventOccurrence = eventRaised.EventOccurrence,
                                        EventSequence = eventRaised.EventSequence,
                                        EventTime = eventRaised.EventTime,
                                        EventTimeUtc = eventRaised.EventTimeUtc,
                                        EventType = eventRaised.GetType().FullName,
                                        MachineName = appInfo.MachineName,
                                        Message = eventRaised.Message,
                                        Details = details,
                                        ExceptionType = exceptionType,
                                        RequestUrl = requestUrl
                                    };
                                ctx.AddToWebEvents(evt);
                                if (Debugger.IsAttached)
                                {
                                    Trace.WriteLine(string.Format("Write - {0} {1}", evt.EventCode, evt.Message));
                                }
                            }

                            ctx.SaveChanges();
                            tx.Commit();
                        }
                        catch (Exception ex)
                        {
                            tx.Rollback();
                            this.LogError(ex);
                        }
                    }
                }
            }
        }

        #endregion

    }
}